package com.lagou.sqlSession;

import com.lagou.config.BoundSql;
import com.lagou.pojo.Configuration;
import com.lagou.pojo.MappedStatement;
import com.lagou.utils.GenericTokenParser;
import com.lagou.utils.ParameterMapping;
import com.lagou.utils.ParameterMappingTokenHandler;
import org.apache.log4j.Logger;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class SimpleExecutor implements Executor {

    private static final Logger log = Logger.getLogger(SimpleExecutor.class);

    /**
     * 执行查询方法
     *
     * @param configuration   配置信息
     * @param mappedStatement SQL语句、参数、返回值等信息
     * @param params          参数
     */
    @Override
    public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception {

        // 获取预处理对象，并设置参数
        PreparedStatement preparedStatement = prepareStatement(configuration, mappedStatement, params);

        // 执行sql
        List<Object> objects = new ArrayList<>();
        try (ResultSet resultSet = preparedStatement.executeQuery()) {
            Class<?> resultType = mappedStatement.getResultType();
            while (resultSet.next()) {
                Object newInstance = resultType.getDeclaredConstructor().newInstance();
                ResultSetMetaData metaData = resultSet.getMetaData();
                for (int i = 1; i <= metaData.getColumnCount(); i++) {
                    // 字段名
                    String columnName = metaData.getColumnName(i);

                    // 字段的值
                    Object value = resultSet.getObject(columnName);

                    // 使用反射或者内省，根据数据库表和实体的对应关系（一一对应），完成封装
                    PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, resultType);
                    Method writeMethod = propertyDescriptor.getWriteMethod();
                    writeMethod.invoke(newInstance, handleResultType(value));
                }
                objects.add(newInstance);
            }
        }
        return (List<E>) objects;
    }

    /**
     * 执行增删改方法
     *
     * @param configuration   配置信息
     * @param mappedStatement SQL语句、参数、返回值等信息
     * @param params          参数
     * @return rowCount 更新成功的行数
     */
    @Override
    public int doUpdate(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception {

        // 获取预处理对象，并设置参数
        PreparedStatement preparedStatement = prepareStatement(configuration, mappedStatement, params);

        // 执行sql
        return preparedStatement.executeUpdate();
    }

    /**
     * 获取预处理对象，并设置参数
     */
    private PreparedStatement prepareStatement(Configuration configuration, MappedStatement mappedStatement,
                                               Object... params)
            throws SQLException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
        // 1 注册驱动，获取连接
        Connection connection = configuration.getDataSource().getConnection();

        // 2.1 获取并转换sql语句
        String sql = mappedStatement.getSql();

        // 2.2 转换sql语句
        BoundSql boundSql = getBoundSql(sql);

        // 3 获取预处理对象
        PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSql());

        // 4.设置参数
        Class<?> parameterType = mappedStatement.getParameterType();
        if (parameterType != null) {
            if (isPrimitive(parameterType)) {
                preparedStatement.setObject(1, params[0]);
            } else {
                List<ParameterMapping> parameterMappings = boundSql.getParameterMappingList();
                for (int i = 0; i < parameterMappings.size(); i++) {
                    ParameterMapping parameterMapping = parameterMappings.get(i);
                    String fieldName = parameterMapping.getContent();
                    // 反射
                    Field declaredField = parameterType.getDeclaredField(fieldName);
                    // 暴力访问
                    declaredField.setAccessible(true);
                    Object value = declaredField.get(params[0]);
                    preparedStatement.setObject(i + 1, handleParameterType(value));
                }
            }
        }
        log.debug("sql = " + boundSql.getSql() + ", parameter = " + params);
        return preparedStatement;
    }

    /**
     * 将带占位符 #{} 的 SQL 语句转换为 JDBC 所需的带 ? 的 SQL，并解析出 #{} 里的值进行存储
     */
    private BoundSql getBoundSql(String sql) {
        // 标记处理类：配置标记解析器来完成对占位符的解析处理工作
        ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
        GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler);
        // 解析出来的sql
        String parseSql = genericTokenParser.parse(sql);
        // #{}里面解析出来的参数名称
        List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();
        return new BoundSql(parseSql, parameterMappings);
    }

    /**
     * 判断参数类型是否为 Java 原始类型
     */
    private boolean isPrimitive(Class<?> parameterType) throws NoSuchFieldException, IllegalAccessException {
        return parameterType == String.class || Number.class.isAssignableFrom(parameterType);
    }

    /**
     * 参数类型处理
     */
    private Object handleParameterType(Object value) {
        if (value == null) {
            return null;
        }
        if (value instanceof Date) {
            return new java.sql.Timestamp(((Date) value).getTime());
        }
        return value;
    }

    /**
     * 返回值类型处理
     */
    private Object handleResultType(Object value) {
        if (value == null) {
            return null;
        }
        if (value instanceof java.sql.Timestamp) {
            return new Date(((java.sql.Timestamp) value).getTime());
        }
        return value;
    }

}
